﻿
using Xacml.Core;
using Xacml.Core.Interfaces;
using Xacml.Core.Policy;
using System.Collections;
using System.Collections.Specialized;

namespace Xacml.Core.Runtime
{
  public class PolicySet : IMatchEvaluable, IObligationsContainer
  {
    private StringCollection _allResources = new StringCollection();
    private IMatchEvaluableCollection _policies = new IMatchEvaluableCollection();
    private Decision _evaluationValue;
    private PolicySetElement _policySet;
    private Target _target;
    private ObligationCollection _obligations;

    public ObligationCollection Obligations
    {
      get
      {
        return this._obligations;
      }
    }

    public StringCollection AllResources
    {
      get
      {
        return this._allResources;
      }
    }

    public PolicySet(EvaluationEngine engine, PolicySetElement policySet)
    {
      this._policySet = policySet;
      if (policySet.Target != null)
      {
        this._target = new Target((TargetElement) policySet.Target);
        foreach (TargetItemBaseReadWrite itemBaseReadWrite in (CollectionBase) policySet.Target.Resources.ItemsList)
        {
          foreach (ResourceMatchElement resourceMatchElement in (CollectionBase) itemBaseReadWrite.Match)
          {
            if (!this._allResources.Contains(resourceMatchElement.AttributeValue.Contents))
              this._allResources.Add(resourceMatchElement.AttributeValue.Contents);
          }
        }
      }
      foreach (object obj in policySet.Policies)
      {
        if (obj is PolicySetElement)
        {
          PolicySet policySet1 = new PolicySet(engine, (PolicySetElement) obj);
          foreach (string str in policySet1.AllResources)
          {
            if (!this._allResources.Contains(str))
              this._allResources.Add(str);
          }
          this._policies.Add((IMatchEvaluable) policySet1);
        }
        else if (obj is PolicyElement)
        {
          Policy policy = new Policy(engine, (PolicyElement) obj);
          foreach (string str in policy.AllResources)
          {
            if (!this._allResources.Contains(str))
              this._allResources.Add(str);
          }
          this._policies.Add((IMatchEvaluable) policy);
        }
        else if (obj is PolicySetIdReferenceElement)
        {
          PolicySetElement policySet1 = engine.Resolve((PolicySetIdReferenceElement) obj);
          if (policySet1 != null)
          {
            PolicySet policySet2 = new PolicySet(engine, policySet1);
            foreach (string str in policySet2.AllResources)
            {
              if (!this._allResources.Contains(str))
                this._allResources.Add(str);
            }
            this._policies.Add((IMatchEvaluable) policySet2);
          }
          else
            throw new EvaluationException(Resource.ResourceManager[Resource.MessageKey.exc_policyset_reference_not_resolved, new object[1]
            {
              (object) ((PolicySetIdReferenceElementReadWrite) obj).PolicySetId
            }]);
        }
        else if (obj is PolicyIdReferenceElement)
        {
          PolicyElement policy1 = engine.Resolve((PolicyIdReferenceElement) obj);
          if (policy1 != null)
          {
            Policy policy2 = new Policy(engine, policy1);
            foreach (string str in policy2.AllResources)
            {
              if (!this._allResources.Contains(str))
                this._allResources.Add(str);
            }
            this._policies.Add((IMatchEvaluable) policy2);
          }
          else
            throw new EvaluationException(Resource.ResourceManager[Resource.MessageKey.exc_policy_reference_not_resolved, new object[1]
            {
              (object) ((PolicyIdReferenceElementReadWrite) obj).PolicyId
            }]);
        }
      }
    }

    public TargetEvaluationValue Match(EvaluationContext context)
    {
      TargetEvaluationValue targetEvaluationValue = TargetEvaluationValue.Match;
      if (this._target != null)
      {
        targetEvaluationValue = this._target.Evaluate(context);
        context.TraceContextValues();
      }
      return targetEvaluationValue;
    }

    public Decision Evaluate(EvaluationContext context)
    {
      context.Trace("Evaluating policySet: {0}", (object) this._policySet.Description);
      context.CurrentPolicySet = this;
      try
      {
        context.Trace("Evaluating Target...");
        context.AddIndent();
        TargetEvaluationValue targetEvaluationValue = this.Match(context);
        context.RemoveIndent();
        context.Trace("Target: {0}", (object) targetEvaluationValue);
        if (targetEvaluationValue == TargetEvaluationValue.Match)
        {
          try
          {
            context.Trace("Evaluating policies...");
            context.AddIndent();
            context.Trace("Policy combination algorithm: {0}", (object) this._policySet.PolicyCombiningAlgorithm);
            IPolicyCombiningAlgorithm combiningAlgorithm = context.Engine.CreatePolicyCombiningAlgorithm(this._policySet.PolicyCombiningAlgorithm);
            if (combiningAlgorithm == null)
              throw new EvaluationException("the policy combining algorithm does not exists.");
            this._evaluationValue = combiningAlgorithm.Evaluate(context, this._policies);
            context.TraceContextValues();
            context.Trace("Policy combination algorithm: {0}", (object) this._evaluationValue.ToString());
          }
          finally
          {
            context.RemoveIndent();
          }
        }
        else if (targetEvaluationValue == TargetEvaluationValue.NoMatch)
          this._evaluationValue = Decision.NotApplicable;
        else if (targetEvaluationValue == TargetEvaluationValue.Indeterminate)
          this._evaluationValue = Decision.Indeterminate;
        context.Trace("PolicySet: {0}", (object) this._evaluationValue);
        this._obligations = new ObligationCollection();
        if (this._evaluationValue != Decision.Indeterminate && this._evaluationValue != Decision.NotApplicable && (this._policySet.Obligations != null && this._policySet.Obligations.Count != 0))
        {
          foreach (ObligationElement obligationElement in (CollectionBase) this._policySet.Obligations)
          {
            if (obligationElement.FulfillOn == Effect.Deny && this._evaluationValue == Decision.Deny || obligationElement.FulfillOn == Effect.Permit && this._evaluationValue == Decision.Permit)
            {
              context.Trace("Adding obligation: {0} ", (object) obligationElement.ObligationId);
              this._obligations.Add((ObligationElementReadWrite) obligationElement);
            }
          }
          foreach (IMatchEvaluable matchEvaluable in (CollectionBase) this._policies)
          {
            IObligationsContainer obligationsContainer = matchEvaluable as IObligationsContainer;
            if (obligationsContainer != null && obligationsContainer.Obligations != null)
            {
              foreach (ObligationElement obligationElement in (CollectionBase) obligationsContainer.Obligations)
              {
                if (obligationElement.FulfillOn == Effect.Deny && this._evaluationValue == Decision.Deny || obligationElement.FulfillOn == Effect.Permit && this._evaluationValue == Decision.Permit)
                  this._obligations.Add((ObligationElementReadWrite) obligationElement);
              }
            }
          }
        }
        return this._evaluationValue;
      }
      finally
      {
        context.CurrentPolicySet = (PolicySet) null;
      }
    }
  }
}
